package org.marverse.scripting

import ScriptingUtil._
import util.Util._

/**
 * Supertype of all function types.
 */
object AnyFunctionType extends Type {
  override def toString = "anyfun"
  override def superTypes: Seq[Type] = Seq(TypeType)
  override def memberDeclarations = Seq(
    ValueMember('argumentType, this, TypeType, _.asInstanceOf[FunctionType].argumentType), 
    ValueMember('returnType, this, TypeType, _.asInstanceOf[FunctionType].returnType)
  )
  override def isInstance(value: Any) = value.isInstanceOf[FunctionType]
}

/**
 * The type of a function.
 */
abstract case class FunctionType(val argumentType: Type, val returnType: Type) extends Type {
  override def toString = "fun[%s, %s]".format(argumentType, returnType)
  
  override def memberDeclarations = Seq.empty
  override def isAssignableFrom(that: Type, ctx: MutableTypedMap[TypedKey, Any]) = 
    that match {
      case FunctionType(thatArg, thatRet) =>
        thatArg.isAssignableFrom(argumentType, ctx) && returnType.isAssignableFrom(thatRet, ctx)
      case _ => super.isAssignableFrom(that, ctx)
    }
  // NOTE: this implementation does not the types or argument and return value
  override def isInstance(value: Any) = value.isInstanceOf[Function]
}

object FunctionType {
  /**
   * Returns a function type with the given argument and return type.
   */
  def apply(argumentType: Type, returnType: Type): FunctionType =
    new FunctionType(argumentType, returnType) {}
  /**
   * Returns a curried function type with the given argument types and 
   * return type. The call <code>FunctionType(t<sub>1</sub>, t<sub>2</sub>, ..., t<sub>n</sub>)</code>
   * is equivalent to <code>FunctionType(t<sub>1</sub>, FunctionType(t<sub>2</sub>, FunctionType(..., t<sub>n</sub>)))</code>.
   */
  def apply(arg1: Type, arg2: Type, arg3: Type, rest: Type*): FunctionType =
    if (rest.isEmpty)
      FunctionType(arg1, FunctionType(arg2, arg3))
    else
      FunctionType(arg1, FunctionType(arg2, arg3, rest.first, rest.drop(1) :_*))
}

/**
 * The base for all functions. Functions are values, too.
 */
trait Function {
  /**
   * Applies the value <code>argument</code> to this function.
   */
  def apply(argument: Any)(implicit ctx: MutableTypedMap[TypedKey, Any]): Any
}

